<template>
  <div class="three-container">
    <h1>Three.js 3D 方块控制</h1>
    
    <!-- 控制说明 -->
    <div class="controls-info">
      <h3>操作说明：</h3>
      
      <div class="control-section">
        <h4>🎮 方块控制：</h4>
        <div class="control-item">
          <span class="key">↑</span> 方块向前移动
        </div>
        <div class="control-item">
          <span class="key">↓</span> 方块向后移动
        </div>
        <div class="control-item">
          <span class="key">←</span> 方块向左移动
        </div>
        <div class="control-item">
          <span class="key">→</span> 方块向右移动
        </div>
        <div class="control-item">
          <span class="key">Space</span> 方块向上移动
        </div>
        <div class="control-item">
          <span class="key">Shift</span> 方块向下移动
        </div>
      </div>
      
      <div class="control-section">
        <h4>📷 相机控制：</h4>
        <div class="control-item">
          <span class="key">W</span> 相机向前移动
        </div>
        <div class="control-item">
          <span class="key">S</span> 相机向后移动
        </div>
        <div class="control-item">
          <span class="key">A</span> 相机向左移动
        </div>
        <div class="control-item">
          <span class="key">D</span> 相机向右移动
        </div>
        <div class="control-item">
          <span class="key">Q</span> 相机向上移动
        </div>
        <div class="control-item">
          <span class="key">E</span> 相机向下移动
        </div>
        <div class="control-item">
          <span class="key">鼠标</span> 拖拽旋转视角
        </div>
      </div>
    </div>
    
    <!-- 3D 场景容器 -->
    <div ref="threeContainer" class="three-scene"></div>
    
    <!-- 状态信息 -->
    <div class="status-info">
      <div class="status-row">
        <p>方块位置: X: {{ cubePosition.x.toFixed(1) }}, Y: {{ cubePosition.y.toFixed(1) }}, Z: {{ cubePosition.z.toFixed(1) }}</p>
        <button @click="resetPosition" class="reset-btn">重置方块</button>
      </div>
      <div class="status-row">
        <p>相机位置: X: {{ cameraControl.position.x.toFixed(1) }}, Y: {{ cameraControl.position.y.toFixed(1) }}, Z: {{ cameraControl.position.z.toFixed(1) }}</p>
        <button @click="resetCamera" class="reset-btn camera-btn">重置相机</button>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive } from 'vue'
import * as THREE from 'three'

// Three.js 相关变量
let scene: THREE.Scene
let camera: THREE.PerspectiveCamera
let renderer: THREE.WebGLRenderer
let cube: THREE.Mesh
let animationId: number

// DOM 引用
const threeContainer = ref<HTMLElement>()

// 方块位置状态
const cubePosition = reactive({
  x: 0,
  y: 0,
  z: 0
})

// 移动速度
const moveSpeed = 0.2

// 键盘状态
const keys = reactive({
  // 方块控制
  up: false,
  down: false,
  left: false,
  right: false,
  space: false,
  shift: false,
  // 相机控制
  w: false,
  s: false,
  a: false,
  d: false,
  q: false,
  e: false
})

// 鼠标控制状态
const mouse = reactive({
  x: 0,
  y: 0,
  isDown: false,
  sensitivity: 0.002
})

// 相机控制参数
const cameraControl = reactive({
  position: { x: 5, y: 5, z: 5 },
  rotation: { x: 0, y: 0 },
  speed: 0.3
})

// 初始化 Three.js 场景
const initThreeJS = () => {
  if (!threeContainer.value) return

  // 创建场景
  scene = new THREE.Scene()
  scene.background = new THREE.Color(0x222222)

  // 创建相机
  camera = new THREE.PerspectiveCamera(
    75,
    threeContainer.value.clientWidth / threeContainer.value.clientHeight,
    0.1,
    1000
  )
  camera.position.set(cameraControl.position.x, cameraControl.position.y, cameraControl.position.z)
  camera.lookAt(0, 0, 0)

  // 创建渲染器
  renderer = new THREE.WebGLRenderer({ antialias: true })
  renderer.setSize(threeContainer.value.clientWidth, threeContainer.value.clientHeight)
  renderer.shadowMap.enabled = true
  renderer.shadowMap.type = THREE.PCFSoftShadowMap
  threeContainer.value.appendChild(renderer.domElement)

  // 创建光源
  const ambientLight = new THREE.AmbientLight(0x404040, 0.6)
  scene.add(ambientLight)

  const directionalLight = new THREE.DirectionalLight(0xffffff, 1)
  directionalLight.position.set(10, 10, 5)
  directionalLight.castShadow = true
  directionalLight.shadow.mapSize.width = 2048
  directionalLight.shadow.mapSize.height = 2048
  scene.add(directionalLight)

  // 创建方块
  const geometry = new THREE.BoxGeometry(1, 1, 1)
  const material = new THREE.MeshLambertMaterial({ 
    color: 0x00ff88,
    transparent: true,
    opacity: 0.9
  })
  cube = new THREE.Mesh(geometry, material)
  cube.castShadow = true
  cube.receiveShadow = true
  scene.add(cube)

  // 创建地面
  const planeGeometry = new THREE.PlaneGeometry(20, 20)
  const planeMaterial = new THREE.MeshLambertMaterial({ color: 0x666666 })
  const plane = new THREE.Mesh(planeGeometry, planeMaterial)
  plane.rotation.x = -Math.PI / 2
  plane.position.y = -2
  plane.receiveShadow = true
  scene.add(plane)

  // 创建网格辅助线
  const gridHelper = new THREE.GridHelper(20, 20, 0x444444, 0x444444)
  gridHelper.position.y = -1.9
  scene.add(gridHelper)

  // 创建坐标轴辅助线
  const axesHelper = new THREE.AxesHelper(5)
  scene.add(axesHelper)

  // 开始动画循环
  animate()
}

// 动画循环
const animate = () => {
  animationId = requestAnimationFrame(animate)

  // 处理键盘输入
  handleMovement()
  handleCameraMovement()

  // 更新方块位置状态
  cubePosition.x = cube.position.x
  cubePosition.y = cube.position.y
  cubePosition.z = cube.position.z

  // 更新相机位置状态
  cameraControl.position.x = camera.position.x
  cameraControl.position.y = camera.position.y
  cameraControl.position.z = camera.position.z

  // 让方块缓慢旋转
  cube.rotation.x += 0.01
  cube.rotation.y += 0.01

  // 渲染场景
  renderer.render(scene, camera)
}

// 处理方块移动逻辑
const handleMovement = () => {
  if (keys.up) cube.position.z -= moveSpeed
  if (keys.down) cube.position.z += moveSpeed
  if (keys.left) cube.position.x -= moveSpeed
  if (keys.right) cube.position.x += moveSpeed
  if (keys.space) cube.position.y += moveSpeed
  if (keys.shift) cube.position.y -= moveSpeed
}

// 处理相机移动逻辑
const handleCameraMovement = () => {
  const speed = cameraControl.speed
  
  // 获取相机的前进方向
  const forward = new THREE.Vector3()
  camera.getWorldDirection(forward)
  
  // 获取相机的右侧方向
  const right = new THREE.Vector3()
  right.crossVectors(forward, camera.up).normalize()
  
  // 前后移动
  if (keys.w) {
    camera.position.addScaledVector(forward, speed)
  }
  if (keys.s) {
    camera.position.addScaledVector(forward, -speed)
  }
  
  // 左右移动
  if (keys.a) {
    camera.position.addScaledVector(right, -speed)
  }
  if (keys.d) {
    camera.position.addScaledVector(right, speed)
  }
  
  // 上下移动
  if (keys.q) {
    camera.position.y += speed
  }
  if (keys.e) {
    camera.position.y -= speed
  }
}

// 键盘事件处理
const handleKeyDown = (event: KeyboardEvent) => {
  switch (event.code) {
    // 方块控制
    case 'ArrowUp':
      keys.up = true
      break
    case 'ArrowDown':
      keys.down = true
      break
    case 'ArrowLeft':
      keys.left = true
      break
    case 'ArrowRight':
      keys.right = true
      break
    case 'Space':
      event.preventDefault()
      keys.space = true
      break
    case 'ShiftLeft':
    case 'ShiftRight':
      keys.shift = true
      break
    // 相机控制
    case 'KeyW':
      keys.w = true
      break
    case 'KeyS':
      keys.s = true
      break
    case 'KeyA':
      keys.a = true
      break
    case 'KeyD':
      keys.d = true
      break
    case 'KeyQ':
      keys.q = true
      break
    case 'KeyE':
      keys.e = true
      break
  }
}

const handleKeyUp = (event: KeyboardEvent) => {
  switch (event.code) {
    // 方块控制
    case 'ArrowUp':
      keys.up = false
      break
    case 'ArrowDown':
      keys.down = false
      break
    case 'ArrowLeft':
      keys.left = false
      break
    case 'ArrowRight':
      keys.right = false
      break
    case 'Space':
      keys.space = false
      break
    case 'ShiftLeft':
    case 'ShiftRight':
      keys.shift = false
      break
    // 相机控制
    case 'KeyW':
      keys.w = false
      break
    case 'KeyS':
      keys.s = false
      break
    case 'KeyA':
      keys.a = false
      break
    case 'KeyD':
      keys.d = false
      break
    case 'KeyQ':
      keys.q = false
      break
    case 'KeyE':
      keys.e = false
      break
  }
}

// 重置位置
const resetPosition = () => {
  if (cube) {
    cube.position.set(0, 0, 0)
  }
}

// 重置相机
const resetCamera = () => {
  if (camera) {
    camera.position.set(5, 5, 5)
    camera.lookAt(0, 0, 0)
    cameraControl.rotation.x = 0
    cameraControl.rotation.y = 0
  }
}

// 鼠标事件处理
const handleMouseDown = (event: MouseEvent) => {
  mouse.isDown = true
  mouse.x = event.clientX
  mouse.y = event.clientY
}

const handleMouseUp = () => {
  mouse.isDown = false
}

const handleMouseMove = (event: MouseEvent) => {
  if (!mouse.isDown) return
  
  const deltaX = event.clientX - mouse.x
  const deltaY = event.clientY - mouse.y
  
  // 更新相机旋转
  cameraControl.rotation.y -= deltaX * mouse.sensitivity
  cameraControl.rotation.x -= deltaY * mouse.sensitivity
  
  // 限制垂直旋转角度
  cameraControl.rotation.x = Math.max(-Math.PI / 2, Math.min(Math.PI / 2, cameraControl.rotation.x))
  
  // 应用旋转到相机
  const spherical = new THREE.Spherical()
  const target = new THREE.Vector3(0, 0, 0)
  
  spherical.setFromVector3(camera.position.clone().sub(target))
  spherical.theta = cameraControl.rotation.y
  spherical.phi = Math.PI / 2 - cameraControl.rotation.x
  
  camera.position.setFromSpherical(spherical).add(target)
  camera.lookAt(target)
  
  mouse.x = event.clientX
  mouse.y = event.clientY
}

// 窗口大小调整
const handleResize = () => {
  if (!threeContainer.value || !camera || !renderer) return
  
  camera.aspect = threeContainer.value.clientWidth / threeContainer.value.clientHeight
  camera.updateProjectionMatrix()
  renderer.setSize(threeContainer.value.clientWidth, threeContainer.value.clientHeight)
}

// 生命周期钩子
onMounted(() => {
  console.log('Three.js 页面已挂载')
  
  // 延迟初始化，确保 DOM 元素已渲染
  setTimeout(() => {
    initThreeJS()
  }, 100)
  
  // 添加事件监听器
  window.addEventListener('keydown', handleKeyDown)
  window.addEventListener('keyup', handleKeyUp)
  window.addEventListener('resize', handleResize)
  
  // 添加鼠标事件监听器
  if (threeContainer.value) {
    threeContainer.value.addEventListener('mousedown', handleMouseDown)
    threeContainer.value.addEventListener('mouseup', handleMouseUp)
    threeContainer.value.addEventListener('mousemove', handleMouseMove)
    threeContainer.value.addEventListener('mouseleave', handleMouseUp)
  }
})

onUnmounted(() => {
  // 清理资源
  if (animationId) {
    cancelAnimationFrame(animationId)
  }
  
  // 移除事件监听器
  window.removeEventListener('keydown', handleKeyDown)
  window.removeEventListener('keyup', handleKeyUp)
  window.removeEventListener('resize', handleResize)
  
  // 移除鼠标事件监听器
  if (threeContainer.value) {
    threeContainer.value.removeEventListener('mousedown', handleMouseDown)
    threeContainer.value.removeEventListener('mouseup', handleMouseUp)
    threeContainer.value.removeEventListener('mousemove', handleMouseMove)
    threeContainer.value.removeEventListener('mouseleave', handleMouseUp)
  }
  
  // 清理 Three.js 资源
  if (renderer) {
    renderer.dispose()
  }
})
</script>

<style scoped>
.three-container {
  padding: 20px;
  max-width: 1200px;
  margin: 0 auto;
  background: linear-gradient(135deg, #1e3c72 0%, #2a5298 100%);
  border-radius: 15px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
}

h1 {
  color: #ffffff;
  text-align: center;
  margin-bottom: 30px;
  text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
  font-size: 2.5em;
}

.controls-info {
  background: rgba(255, 255, 255, 0.95);
  padding: 20px;
  border-radius: 12px;
  margin-bottom: 20px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}

.controls-info h3 {
  color: #2c3e50;
  margin-bottom: 20px;
  text-align: center;
  font-size: 1.4em;
}

.control-section {
  margin-bottom: 20px;
  padding: 15px;
  background: rgba(52, 152, 219, 0.05);
  border-radius: 8px;
  border-left: 4px solid #3498db;
}

.control-section h4 {
  color: #2c3e50;
  margin-bottom: 10px;
  font-size: 1.1em;
  text-align: center;
}

.control-item {
  display: inline-block;
  margin: 8px 15px;
  font-size: 14px;
  color: #34495e;
}

.key {
  display: inline-block;
  background: linear-gradient(145deg, #f0f0f0, #e0e0e0);
  border: 2px solid #ccc;
  border-radius: 6px;
  padding: 4px 8px;
  margin-right: 8px;
  font-family: 'Courier New', monospace;
  font-weight: bold;
  color: #2c3e50;
  box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
  min-width: 30px;
  text-align: center;
}

.three-scene {
  width: 100%;
  height: 600px;
  border-radius: 12px;
  overflow: hidden;
  box-shadow: 0 8px 25px rgba(0, 0, 0, 0.2);
  border: 3px solid rgba(255, 255, 255, 0.2);
  margin-bottom: 20px;
}

.status-info {
  background: rgba(255, 255, 255, 0.95);
  padding: 20px;
  border-radius: 12px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}

.status-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: 15px;
  margin-bottom: 15px;
}

.status-row:last-child {
  margin-bottom: 0;
}

.status-info p {
  font-size: 16px;
  color: #2c3e50;
  margin: 0;
  font-family: 'Courier New', monospace;
  font-weight: bold;
}

.reset-btn {
  background: linear-gradient(145deg, #e74c3c, #c0392b);
  color: white;
  border: none;
  padding: 12px 24px;
  border-radius: 8px;
  cursor: pointer;
  font-size: 14px;
  font-weight: bold;
  transition: all 0.3s ease;
  box-shadow: 0 4px 12px rgba(231, 76, 60, 0.3);
}

.reset-btn:hover {
  background: linear-gradient(145deg, #c0392b, #a93226);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px rgba(231, 76, 60, 0.4);
}

.reset-btn:active {
  transform: translateY(0);
}

.camera-btn {
  background: linear-gradient(145deg, #3498db, #2980b9);
  box-shadow: 0 4px 12px rgba(52, 152, 219, 0.3);
}

.camera-btn:hover {
  background: linear-gradient(145deg, #2980b9, #1f618d);
  box-shadow: 0 6px 18px rgba(52, 152, 219, 0.4);
}

.three-scene {
  cursor: grab;
}

.three-scene:active {
  cursor: grabbing;
}

@media (max-width: 768px) {
  .three-container {
    padding: 15px;
  }
  
  .three-scene {
    height: 400px;
  }
  
  .status-row {
    flex-direction: column;
    text-align: center;
  }
  
  .status-row p {
    margin-bottom: 10px;
  }
  
  .control-item {
    display: block;
    margin: 5px 0;
    text-align: center;
  }
  
  h1 {
    font-size: 2em;
  }
}
</style>
